/*
******************************
* Copyright by Maxic Tech                                                      *
* All rights are reserved.
******************************
*/

#include <stdio.h>
#include <string.h>
#include "pico/stdlib.h"
#include "pico/binary_info.h"
#include "hardware/i2c.h"
#include "maxic_mt320x.h"
#include "maxic_i2c.h"
#include "maxic_mt320x_als.h"

#define mt320x_debug_level 1
#define _DRV_TAG_ "maxic_mt320x.c"

#define mt320x_dbg(fmt, args...)                                             \
	do                                                                       \
	{                                                                        \
		if (mt320x_debug_level)                                              \
		{                                                                    \
			printf("mt320x: " _DRV_TAG_ " %d " fmt " \n", __LINE__, ##args); \
		}                                                                    \
	} while (0)
#define mt320x_info(fmt, args...)                                        \
	do                                                                   \
	{                                                                    \
		printf("mt320x: " _DRV_TAG_ " %d " fmt " \n", __LINE__, ##args); \
	} while (0)
#define mt320x_err(fmt, args...)                                                \
	do                                                                          \
	{                                                                           \
		printf("mt320x: error: " _DRV_TAG_ " %d " fmt " \n", __LINE__, ##args); \
	} while (0)

struct mt320x_chip mt320x_t_obj;
struct mt320x_chip *mt320x_obj = &mt320x_t_obj;
uint8_t MT320X_INT_FLAG = 1;

static mt320x_parameters mt320x_default_param = {
	.als_time = ATIME_MS(50),	   // 0x81 reg  100ms --> @0x23 value
	.await_time = WTIME_MS(50),	   // 0x83 reg  @0x11 value
	.als_th_low = 0x0000,		   //[0x85,0x84] reg
	.als_th_high = 0xFFFF,		   //[0x87,0x86] reg
	.persistence = ALS_PERSIST(0), // 0x8c reg  @0x00 value
};

int32_t mt320x_dump_registers(struct mt320x_chip *mt320x_obj)
{
	uint8_t i, j, k;
	const int32_t NoPerLine = 16;
	char line_buf[100];
	int32_t line_len = 0;
	uint8_t reg_val = 0;

	uint16_t buf_len = 256;

	mt320x_dbg("------------------mt320x register dump start-------------------");

	line_len += sprintf(line_buf, "%s", "     ");

	for (i = 0; i < 0x10; i++)
	{
		line_len += sprintf(line_buf + line_len, "  %2x", i);
	}
	mt320x_dbg("%s ", line_buf);

	for (i = 0; i < buf_len / NoPerLine; i++)
	{
		memset(line_buf, 0, sizeof(line_buf));
		line_len = 0;

		for (j = 0; j < NoPerLine; j++)
		{
			k = i * NoPerLine + j;
			maxic_i2c_read(k, &reg_val);

			if (k >= buf_len)
			{
				break;
			}
			line_len += sprintf(line_buf + line_len, "%02x, ", reg_val);
		}
		mt320x_dbg("[0x%02x] %s ", i * NoPerLine, line_buf);
	}

	mt320x_dbg("------------------mt320x register dump end-------------------");

	return 0;
}

int32_t mt320x_do_initialization(struct mt320x_chip *mt320x_obj)
{
	// do a power on reset
	maxic_i2c_modify(MT320X_REG_SOFTRST, POR, POR);
	mt320x_err("do a power on reset, ingore i2c error print");

#ifdef MT320X_DEBUG
	mt320x_dbg("dump register values after POR");
	mt320x_dump_registers(mt320x_obj);
#endif

	// copy default param to mt320x_obj->params
	memcpy(&mt320x_obj->params, &mt320x_default_param, sizeof(mt320x_parameters));

	// reg init from mt320x_default_param
	maxic_i2c_write(MT320X_REG_ATIME, mt320x_obj->params.als_time);
	maxic_i2c_write(MT320X_REG_AWTIME, mt320x_obj->params.await_time);

	mt320x_dbg("als_time = 0x%02x,  await_time = 0x%02x", mt320x_obj->params.als_time, mt320x_obj->params.await_time);

	maxic_i2c_write(MT320X_REG_AILTL, (uint8_t)(mt320x_obj->params.als_th_low & 0xFF));
	maxic_i2c_write(MT320X_REG_AILTH, (uint8_t)(mt320x_obj->params.als_th_low >> 8));
	maxic_i2c_write(MT320X_REG_AIHTL, (uint8_t)(mt320x_obj->params.als_th_high & 0xFF));
	maxic_i2c_write(MT320X_REG_AIHTH, (uint8_t)(mt320x_obj->params.als_th_high >> 8));
	maxic_i2c_write(MT320X_REG_ALS_PERS, mt320x_obj->params.persistence);

	// set default als gain
	// we should set these parameter because als  calibration will use this, even though haven't open/close sensor
	mt320x_set_als_gain(mt320x_obj, 16); // set default als gain 16x

	// other reg init
	maxic_i2c_modify(MT320X_REG_GAIN_CTRL3, ALS_GAIN_MASK, ALS_GAIN_2x); // 0xA6 only set als is ok  @reg value is 0x04
	maxic_i2c_modify(MT320X_REG_TESTCFG2, DARKIEN, DARKIEN);			 // 0xB2    @reg value is 0x0f
	maxic_i2c_modify(MT320X_REG_TESTCFG4, PCOFFSKIP, PCOFFSKIP);		 // 0xB4	 @reg value is 0x08
	maxic_i2c_modify(MT320X_REG_ANACFG1, ALS_GT_CTRL, 0x01);			 // 0xB5    @reg value is 0x01

	// other parameter initial
	mt320x_obj->als_enabled = false;
	mt320x_obj->als_inf.als_factor = 1000;
	mt320x_obj->als_gain_auto = true;
	mt320x_obj->find_proper_gain = false;

#ifdef MT320X_DEBUG
	mt320x_dbg("dump register values after reg init");
	mt320x_dump_registers(mt320x_obj);
#endif

	return 0;
}

void mt320x_check_chip_id(void)
{
	uint8_t part_id = 0, manu_id = 0;

	maxic_i2c_read(MT320X_REG_PART_ID, &part_id);
	maxic_i2c_read(MT320X_REG_MANU_ID, &manu_id);
	mt320x_info("mt320x part_id = 0x%02x, manu_id = 0x%02x", part_id, manu_id);

	if ((part_id != 0x92) && (manu_id != 0x05))
	{
		mt320x_err("read mt320x chip id error!");
	}
	else
	{
		mt320x_info("read mt320x chip id sucessfully!");
	}
}

void mt320x_set_work_mode(struct mt320x_chip *mt320x_obj, int type)
{
	switch (type)
	{
	case OPEN_ALS:
		mt320x_dbg("open als work mode");
		mt320x_als_enable(mt320x_obj);
		break;
	default:
		mt320x_err("wrong work mode");
		break;
	}
}

void mt320x_process_als_data(struct mt320x_chip *mt320x_obj)
{

	uint8_t reg_astatus;

	maxic_i2c_read(MT320X_REG_ASTATUS, &reg_astatus);

	mt320x_dbg(""); // indicate a new cycle

	mt320x_dbg("als status reg 0x9B = 0x%02x", reg_astatus);

	do
	{
		if (reg_astatus & AINT)
		{
			if (!mt320x_obj->polling_mode)
			{ // interrupt mode
				if (mt320x_obj->find_proper_gain == true)
				{
					mt320x_obj->find_proper_gain = false;
					mt320x_dbg("skip read als when finding proper gain is in going");
				}
				else
				{
					mt320x_read_als(mt320x_obj);
					mt320x_get_lux(mt320x_obj);
					if (mt320x_obj->als_is_ready == true)
					{
						if (mt320x_obj->first_als == true)
						{
							mt320x_obj->first_als = false;		// first_als is only useful for interrupt mode. for polling mode, first_als is not used
							mt320x_dbg("set als persist to 1"); // will be effective in next integration cycle
							maxic_i2c_modify(MT320X_REG_ALS_PERS, APERS_MASK, ALS_PERSIST(1));
						}
					}
					mt320x_update_als_thresh(mt320x_obj);
				}
			}
			else
			{ // polling mode
				if (mt320x_obj->find_proper_gain == true)
				{
					mt320x_obj->find_proper_gain = false;
					mt320x_dbg("skip read als when finding proper gain is in going in polling mode");
				}
				else
				{
					mt320x_read_als(mt320x_obj);
					mt320x_get_lux(mt320x_obj);
				}
			}

			maxic_i2c_write(MT320X_REG_ASTATUS, AINT); // clear AINT after reading data
		}

		maxic_i2c_read(MT320X_REG_ASTATUS, &reg_astatus);

		reg_astatus &= AINT;
		mt320x_dbg("reg_astatus & AINT = %d", reg_astatus & AINT);

	} while (reg_astatus != 0); // in case when clear AINT comes, process AINT in one time

	return;
}

int main()
{
	stdio_init_all();
#if !defined(i2c_default) || !defined(PICO_DEFAULT_I2C_SDA_PIN) || !defined(PICO_DEFAULT_I2C_SCL_PIN)
#warning i2c/MT320X_i2c example requires a board with I2C pins
	puts("Default I2C pins were not defined");
#else
	printf("Hello, MT320X! Reading raw data from registers...\n");

	gpio_init(PICO_DEFAULT_LED_PIN);
	gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT);
	gpio_put(PICO_DEFAULT_LED_PIN, 0);

	// This example will use I2C0 on the default SDA and SCL pins (4, 5 on a Pico)
	i2c_init(i2c_default, 100 * 1000);
	gpio_set_function(PICO_DEFAULT_I2C_SDA_PIN, GPIO_FUNC_I2C);
	gpio_set_function(PICO_DEFAULT_I2C_SCL_PIN, GPIO_FUNC_I2C);
	// gpio_pull_up(PICO_DEFAULT_I2C_SDA_PIN);
	// gpio_pull_up(PICO_DEFAULT_I2C_SCL_PIN);
	// Make the I2C pins available to picotool
	bi_decl(bi_2pins_with_func(PICO_DEFAULT_I2C_SDA_PIN, PICO_DEFAULT_I2C_SCL_PIN, GPIO_FUNC_I2C));

	printf("start MT320X init \r\n");

	// 1.chipID check
	mt320x_check_chip_id();

	// 2. reset mt320x_obj
	memset(mt320x_obj, 0, sizeof(*mt320x_obj));

	// 3. choose work mode, polling mode or interrupt mode
	mt320x_obj->polling_mode = true; // true: polling mode  false: interrupt mode,

	// 4. do sensor initialization
	mt320x_do_initialization(mt320x_obj);

	// 5. set work mode: OPEN_ALS
	mt320x_set_work_mode(mt320x_obj, OPEN_ALS);

	printf("MT320X init finished, start to process sensor data in while(1) \r\n");

	while (1)
	{
		// 1.chipID check
		mt320x_check_chip_id();
		sleep_ms(1000);
	}
	while (1)
	{
		// 6.1 process sensor data, polling mode
		if (mt320x_obj->polling_mode == true)
		{
			gpio_put(PICO_DEFAULT_LED_PIN, 0);
			mt320x_process_als_data(mt320x_obj);
			// sleep_us(500); // this can add or delete, only used to control the framerate,may cause delay to process INT
			gpio_put(PICO_DEFAULT_LED_PIN, 1);
		}

		// 6.2 process sensor data, interrupt mode
		if ((MT320X_INT_FLAG == 2) && (mt320x_obj->polling_mode == false))
		{
			MT320X_INT_FLAG = 1;
			mt320x_process_als_data(mt320x_obj);
			sleep_us(100); // this can add or delete, only used to control the framerate, may cause delay to process INT
		}
	}
#endif
}
